#ifndef GAMEENGINE_GRAPHICS_SHADER_PROGRAM_H
#define GAMEENGINE_GRAPHICS_SHADER_PROGRAM_H

#include <GameEngine/Graphics/graphics_settings.h>

#include <GameEngine/Graphics/Shaders/shader_defs.h>
#include <GameEngine/Graphics/Shaders/shader_object.h>
#include <GameEngine/Core/Resource/ptr_resource.h>
#include <GameEngine/Core/Classes/rtti.h>

#include <list>


#ifdef GAMEENGINE_NAMESPACES
namespace GameEngine {
#endif

//========================================================


#ifndef DOXYGEN_IGNORE

//! Class in charge of handling shader program data
/*!
 *  This class is meant to be used internally by the GLSLShaderProgram class and
 *  should never be used by client code.
 *
 @note This class must always be manipulated AFTER a valid OpenGL context has been created. If
       no OpenGL context is available, any call to any function or constructor of this class
	   will result in an application crash.
 */
class _Graphics_decl GLSLShaderProgramData {
	//! The shader program data class is friend of the shader program class to allow direct data access
	friend class GLSLShaderProgram;

public:
	GLSLShaderProgramData();
	virtual ~GLSLShaderProgramData();

protected:
	//! Identifier of the shader program
	GLuint shader_program_;

	//! List of shader objects associated to the shader program
	GAMEENGINE_FORCE_DLL_CLASS_STD(vector,GLSLShaderObject) objects_;
};

#endif


//========================================================

//! Class in charge of holding shader programs written in GLSL
/*!
 *  Shaders programs are small pieces of code executed directly on the graphic processing unit (GPU). They allow a great
 *  flexibility in the treatment of graphic primitives, using a SIMD (Single Instruction Multiple Data)
 *  architecture. The shaders can be compiled and linked at run-time, allowing to change dynamically their content
 *  while the main application is running.
 *
 *  GLSL stands for the "OpenGL Shading Language". Its syntax is close to the C language. An excellent overview of the
 *  GLSL language can be found in Randi Rost's book, "OpenGL Shading Language, second edition", aka the "Orange book"
 *
 *  The shader programs are handled as resources, which allows them to be quickly and safely copied / deleted.
 *
 @sa The GLSLShaderObject class, that allows to manipulate shader objects (i.e., small pieces of compiled source code).
 @note This class must always be manipulated AFTER a valid OpenGL context has been created. If
       no OpenGL context is available, any call to any function or constructor of this class
	   will result in an application crash.
 */
class _Graphics_decl GLSLShaderProgram : public PtrResource<GLSLShaderProgramData> {
	GAMEENGINE_DECLARE_RTTI_SINGLE(GLSLShaderProgram)
	//! The resource manager class is friend of the shader program class because it must be able to access its ID and context
	friend class ResourceManager<ID,CONTEXTID>;

public:
	//! Typedef used for convenient manipulation of the shader program resource type
	typedef PtrResource<GLSLShaderProgramData> RESOURCE_TYPE;
	//! Typedef for shader programs resource managers
	typedef ResourceManager<ID,CONTEXTID> ShaderProgramResourceManager;

public:
	//! Input primitive type for geometry shaders
	enum GeometryShaderInputType {
		//! Input point primitives
		PointsIn = GL_POINTS,
		//! Input line primitives
		LinesIn = GL_LINES,
		//! Input line primitives with adjacency informations
		LinesAdjacencyIn = GL_LINES_ADJACENCY_EXT,
		//! Input triangle primitives
		TrianglesIn = GL_TRIANGLES,
		//! Input triangle primitives with adjacency informations
		TrianglesAdjacencyIn = GL_TRIANGLES_ADJACENCY_EXT
	};
	//! Output primitive type for geometry shaders
	enum GeometryShaderOutputType {
		//! Output point primitives
		PointsOut = GL_POINTS,
		//! Output line strip primitives
		LineStripOut = GL_LINE_STRIP,
		//! Output triangle strip primitives
		TriangleStripOut = GL_TRIANGLE_STRIP
	};

public:
	GLSLShaderProgram();
	GLSLShaderProgram(const char* vertex_source, const char* geometry_source, const char* fragment_source, bool from_file=true,
		bool link_immediately = true, const GeometryShaderInputType& input_type = PointsIn,
		const GeometryShaderOutputType& output_type = TriangleStripOut, unsigned int vertices_count = 12);
	GLSLShaderProgram(const GLSLShaderProgram& rhs);
	virtual ~GLSLShaderProgram();
	GLSLShaderProgram& operator=(const GLSLShaderProgram& rhs);

	bool set_shader(const char* vertex_source, const char* geometry_source, const char* fragment_source, bool from_file=true,
					bool link_immediately = true, const GeometryShaderInputType& input_type = PointsIn,
					const GeometryShaderOutputType& output_type = TriangleStripOut, unsigned int vertices_count = 12);
	bool set_geometry_parameters(const GeometryShaderInputType& input_type = PointsIn,
					const GeometryShaderOutputType& output_type = TriangleStripOut, unsigned int vertices_count = 12);

	void enable() const;
	void disable() const;

	bool add_object(const GLSLShaderObject& object);
	bool find_object(const GLSLShaderObject& object) const;
	bool remove_object(const GLSLShaderObject& object);
	bool remove_all_objects();

	bool link() const;

	GLint get_attribute(const char* name) const;
	GLint get_uniform(const char* name) const;

	void uniform(GLint id, int value) const;
	void uniform(GLint id, float value) const;
	void uniform2f(GLint id, float, float) const;
	void uniform3f(GLint id, float, float, float) const;
	void uniform4f(GLint id, float,float,float,float) const;
	void attribute(GLint id, float value) const;
	void attribute2f(GLint id, float, float) const;
	void attribute3f(GLint id, float, float, float) const;
	void attribute4f(GLint id, float,float,float,float) const;

	static bool check_glew();

private:
	bool check_linking(const char* message) const;

	virtual CONTEXTID context() const;
};


//========================================================


#ifdef GAMEENGINE_NAMESPACES
} // namespace GameEngine
#endif

#endif
